﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Sl.Duplex;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace Sl.Forms {
	public partial class StreamUI : UserControl {

		static StreamUI() {
		}
		public StreamUI() {
			InitializeComponent();
			initButton();
			ImputCRTB.inputCmd = inputCmd;
			helpBTN.Text = cmd_desc;
			setEncoder(gB18030ToolStripMenuItem, EventArgs.Empty);
			ImputCRTB.Text = $"-c1000 $Hello World {DateTime.Now}";
		}
		readonly public string cmd_desc =
@"输入的数据通常以16进制解释，例如：
	12 FE A1 56 /H |255
此外“|”后续的数据以十进制表示；“/”后续的数据以ASCII码显示；
如果整个命令的开头为“$”则以UTF-8编码发送后续的全部内容，例如：
	$Hello World 2022!
命令前可添加可选参数 -c[delayMs][^times] 以启动循环发送，例如：
	-c1000 $Hello World 2022! 表示每1000毫秒发送一次
	-c200^10 12 FE A1 56 /H |255 表示每200毫秒发送一次发送10次后停止
";

		IDuplex port = null;
		public IDuplex Port {
			get {
				return port;
			}
			set {
				if (port != null) {
					port.dDataToRead = null;
				}
				port = value;
				if (port != null) {
					port.dDataToRead = this.receiveData;
				}
			}
		}
		private void receiveData(IDuplex p) {
			byte[] temp = new byte[1024];
			int read_len = p.Read(temp, 0, 1024);
			outputData(temp, 0, read_len);
		}
		public void outputData(byte[] b, int diff = 0, int len = -1) {
			this.BeginInvoke(new Action<byte[], bool, int, int>(streamRTB.outputLine), b, false, diff, len);
		}




		readonly char[] split_strs = { ' ', '\t' };
		private bool inputCmd(string cmd) {
			var report = runCmd(cmd);
			if (report == null) {
				return true;
			} else {
				badCmdTT.Show(report, ImputCRTB, 5000);
				return false;
			}
		}
		private string runCmd(string cmd) {
			if (false == port.IsRunning) {
				return $"没有打开串口。";
			}
			cmd = cmd.Trim();
			string s_payload;
			string s_cycle;
			if ((cmd.Length > 2) && (cmd.Substring(0, 2) == "-c")) {
				s_cycle = cmd.Substring(0, cmd.IndexOf(' ')).Trim();
				s_payload = cmd.Substring(cmd.IndexOf(' ')).Trim();
			} else {
				s_cycle = null;
				s_payload = cmd;
			}
			string warning;
			var data_to_send = strToByteArray(s_payload, out warning);
			if (warning != null) {
				return warning;
			}

			if (s_cycle == null) {
				sendData(data_to_send.ba, 0, data_to_send.len);
			} else {
				string[] ss = s_cycle.Substring(2).Split('^');
				int delay, totle_times;
				if (ss.Length == 2) {
					if (false == int.TryParse(ss[0], out delay)) {
						return $"延时时间设置错误：{ss[0]}。";
					}
					if (false == int.TryParse(ss[1], out totle_times)) {
						return $"次数设置错误：{ss[1]}。";
					}
				} else if (ss.Length == 1) {
					if (false == int.TryParse(ss[0], out delay)) {
						return $"延时时间设置错误：{ss[0]}。";
					}
					totle_times = -1;
				} else {
					return $"-c设置错误。";
				}
				if (is_send_cycle_running) {
					return $"必须等待上一次循环发送完成才能启动下一次。";
				}
				sendDataCycle(delay, totle_times, data_to_send.ba, 0, data_to_send.len);
			}
			return null;
		}



		/// <summary>
		/// 按照规则将字符串转换为对应的数组
		/// </summary>
		/// <param name="str">需要转换的字符串</param>
		/// <returns>如果转换成功，则返回List byte，否则返回错误原因的字符串。</returns>
		public (byte[] ba, int len) strToByteArray(string str, out string warning) {
			warning = null;
			str = str.Trim();
			if (str.Substring(0, 1) == "$") {
				byte[] ba = new byte[4096];
				char[] chars = str.Substring(1).ToCharArray();
				try {
					int len = streamRTB.ec.GetBytes(chars, 0, chars.Length, ba, 0, true);
					return (ba, len);
				} catch (ArgumentException) {
					warning = $"要发送的数据太多了，超过了{ba.Length}Byte。";
					return (null, 0);
				}
			} else {
				List<byte> b_list = new List<byte>();
				//填充数据
				string[] ss = str.Split(split_strs, StringSplitOptions.RemoveEmptyEntries);
				foreach (string s in ss) {
					string first = s.Substring(0, 1);
					byte b;
					if (first == "|") {
						if (false == Byte.TryParse(s.Substring(1), out b)) {
							warning = $"按照十进制数据解析失败。({s})";
							return (null, 0);
						}
					} else if (first == "/") {
						if (s.Length != 2) {
							warning = $"只能转换单个字符({s})";
							return (null, 0);
						}
						b = (byte)(s.Substring(1).ToCharArray()[0]);
					} else {
						if (false == Byte.TryParse(s, System.Globalization.NumberStyles.HexNumber, null, out b)) {
							warning = $"按照十六进制数据解析失败。({s})";
							return (null, 0);
						}
					}
					b_list.Add(b);
				}
				byte[] ba = b_list.ToArray();
				return (ba, ba.Length);
			}
		}



		void sendData(byte[] buffer, int offset, int count) {
			port.Write(buffer, offset, count);
			this.Invoke(new Action<byte[], bool, int, int>(streamRTB.outputLine), buffer, true, offset, count);
		}

		AutoResetEvent stop_send_flag = null;
		bool is_send_cycle_running => stop_send_flag != null;

		Image recycle_open = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.recycle, Color.FromArgb(0, 77, 153));
		Image recycle_close = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.recycle, Color.DarkGray);

		private void recycleBTN_Click(object sender, EventArgs e) {
			if (is_send_cycle_running) {
				stop_send_flag.Set();
			}
		}
		int send_times;
		int send_totle;
		void sendDataCycle(int delay_ms, int totle_times, byte[] buffer, int offset, int count) {
			this.recycleBTN.Image = recycle_open;
			Task.Run(() => {
				try {
					int times = 0;
					send_totle = totle_times;
					while (true) {

						stop_send_flag = new AutoResetEvent(false);
						if (false == port.IsRunning) {
							return;
						}
						port.Write(buffer, offset, count);
						this.Invoke(new Action<byte[], bool, int, int>(streamRTB.outputLine), buffer, true, offset, count);
						times++;
						send_times = times;
						if (stop_send_flag.WaitOne(delay_ms)) {
							return;
						}
						if (times == totle_times) {
							return;
						}
					}
				} finally {
					stop_send_flag.Dispose();
					stop_send_flag = null;
					this.Invoke(new Action(() => this.recycleBTN.Image = recycle_close));
				}
			});
		}





		Image time_open = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.clock, Color.FromArgb(0, 77, 153));
		Image time_close = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.clock, Color.DarkGray);
		private void TimeDBTN_Click(object sender, EventArgs e) {
			TimeDBTN.Image = (streamRTB.Record_time = !streamRTB.Record_time) ?
				time_open : time_close;
		}

		Image ascii_open = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.asc_32px, Color.FromArgb(0, 77, 153));
		Image ascii_close = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.asc_32px, Color.DarkGray);
		private void AscIIDBTN_Click(object sender, EventArgs e) {
			AscIIDBTN.Image = (streamRTB.Record_ascii = !streamRTB.Record_ascii) ?
				ascii_open : ascii_close;
		}
		Image hex_open = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.hexadecimal_32px, Color.FromArgb(0, 77, 153));
		Image hex_close = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.hexadecimal_32px, Color.DarkGray);


		Image text_open = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.page_26px, Color.FromArgb(0, 77, 153));
		Image text_close = Sl.Support.Forms.IconRecolor.reColorPng(global::CmdTextBox.Properties.Resources.page_26px, Color.DarkGray);


		private void HexDBTN_Click(object sender, EventArgs e) {
			HexDBTN.Image = (streamRTB.Record_hex = !streamRTB.Record_hex) ?
				hex_open : hex_close;
		}

		private void initButton() {
			HexDBTN.Image = (streamRTB.Record_hex) ?
				hex_open : hex_close;
			AscIIDBTN.Image = (streamRTB.Record_ascii) ?
				ascii_open : ascii_close;
			TimeDBTN.Image = (streamRTB.Record_time) ?
				time_open : time_close;
			recycleBTN.Image = recycle_close;
		}

		private void sendBTN_Click(object sender, EventArgs e) {
			this.ImputCRTB.send();
		}

		private void helpBTN_Click(object sender, EventArgs e) {

		}
		long last_check_point = -1;
		long s_count_speed_keep = 0;
		long r_count_speed_keep = 0;
		public long Send_count => port.Write_count;
		public long Recv_count => port.Read_count;
		public double Send_speed { get; set; } = 0;
		public double Recv_speed { get; set; } = 0;

		public void CheckSpeedLoop() {
			long check_point = Stopwatch.GetTimestamp();
			if (last_check_point != -1) {
				double delay = (check_point - last_check_point) * 1.0 / Stopwatch.Frequency;
				Send_speed = (Send_count - s_count_speed_keep) / delay;
				Recv_speed = (Recv_count - r_count_speed_keep) / delay;
			}
			s_count_speed_keep = Send_count;
			r_count_speed_keep = Recv_count;
			last_check_point = check_point;
		}


		private void ClearCount(object sender, EventArgs e) {
			port.clearCount();
			s_count_speed_keep = port.Write_count;
			r_count_speed_keep = port.Read_count;
			streamRTB.Clear();
		}
		private void recycleBTN_MouseEnter(object sender, EventArgs e) {
			if (is_send_cycle_running) {
				if (send_totle == -1) {
					recycleBTN.Text = $"终止循环发送\n已发送{send_times}";
				} else {
					recycleBTN.Text = $"终止循环发送\n已发送{send_times}/{send_totle}";
				}
			} else {
				if (send_totle == -1) {
					recycleBTN.Text = $"循环发送已终止\n已发送{send_times}";
				} else {
					recycleBTN.Text = $"循环发送已终止\n已发送{send_times}/{send_totle}";
				}

			}
		}
		private void stringModeSwitch(object sender, EventArgs e) {
			if (streamRTB.String_mode == false) {
				streamRTB.String_mode = true;
				BaseReceiveBTN.Image = text_open;
			} else {
				streamRTB.String_mode = false;
				BaseReceiveBTN.Image = text_close;
			}
		}

		private void setEncoder(object sender, EventArgs e) {
			var sender_bt = sender as ToolStripMenuItem;
			if (sender_bt != null) {
				Encoding encoding;
				System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
				encoding = Encoding.GetEncoding(sender_bt.Text);
				streamRTB.encodingInit(encoding);
			}
		}
	}
}
